package 
{
	import away3d.events.MouseEvent3D;	
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.events.TouchEvent;
	import kizzo.game.RandomGenerator;
	import kizzo.game.tile_objects.ActivatedTile;
	import kizzo.game.tile_objects.ActivatorTile;
	import kizzo.game.tile_objects.TeleportTile;
	import kizzo.game.tile_objects.TileObject;
	import kizzo.game.TileState;
	import kizzo.game.Game;
	import kizzo.geometry.TileCoord3D;
	import kizzo.gui.EditObject;
	import kizzo.gui.LabelledTextInput;
	import kizzo.gui.PropertyMenu;
	import org.flashdevelop.utils.FlashConnect;
	
	
	public class InputController
	{
		private static var instance:InputController;
		private static var allow_instantiation:Boolean;
		
		private var game:Main;	
		private var editor:Editor;
		
		private var input_enabled:Boolean;
		private var input_buffer:Vector.<uint>;
		private var MAX_INPUT_BUFFER_LENGTH:int = 2;
		
		public static function get_instance():InputController 
		{
			if (instance == null) 
			{
				allow_instantiation = true;
				instance = new InputController();
				allow_instantiation = false;
			}
			return instance;
		}
				
		
		public function InputController():void 
		{
			if (!allow_instantiation) 
			{
				throw new Error("Error: Instantiation failed: Use InputController.get_instance() instead of new.");
			}
			
			this.game = Main.get_instance();						
			this.editor = Editor.get_instance();
			this.input_buffer = new Vector.<uint>();
			this.input_enabled = false;

			this.game.stage.addEventListener(KeyboardEvent.KEY_DOWN, game_key_handler);			
			this.game.stage.addEventListener(TouchEvent.TOUCH_END, touch_handler);	
		}
		
		
		public function enable_edit_input():void
		{
			this.game.mouse_picker_plane.addEventListener(MouseEvent3D.DOUBLE_CLICK, dbl_click_handler);	
			this.game.mouse_picker_plane.addEventListener(MouseEvent3D.MOUSE_UP, release_handler);
			this.game.mouse_picker_plane.addEventListener(MouseEvent3D.MOUSE_DOWN, click_handler);	
			this.game.stage.addEventListener(KeyboardEvent.KEY_DOWN, edit_key_handler);			
		}
		
		
		public function disable_edit_input():void
		{
			this.game.mouse_picker_plane.removeEventListener(MouseEvent3D.DOUBLE_CLICK, dbl_click_handler);	
			this.game.mouse_picker_plane.removeEventListener(MouseEvent3D.MOUSE_UP, release_handler);
			this.game.mouse_picker_plane.removeEventListener(MouseEvent3D.MOUSE_DOWN, click_handler);				
			this.game.stage.removeEventListener(KeyboardEvent.KEY_DOWN, edit_key_handler);			
		}		
		
		
		public function enable_game_input():void
		{	
			if (this.input_buffer.length == 0)
			{
				this.input_enabled = true;
			}
			else
			{
				make_move(this.input_buffer.shift());
			}
		}
		
		
		public function disable_game_input():void
		{
			this.input_enabled = false;		
		}	
		
		
		public function flush_input_buffer():void
		{
			this.input_buffer = new Vector.<uint>();
		}
		
		
		private function dbl_click_handler(e:MouseEvent3D):void
		{						
			var x:int = int(Math.round(e.scenePosition.x / Game.TILE));
			var z:int = int(Math.round(e.scenePosition.z / Game.TILE));
			
			var objects:Vector.<TileObject> = TileState(this.game.state_array[x + Game.ARRAY_OFFSET][Game.ARRAY_OFFSET][z + Game.ARRAY_OFFSET]).get_objects();
			
			this.game.addChild(this.editor.edit_menu);	
			
			if (objects.length > 0)			
			{			
				this.editor.edit_menu.add_static_text(new LabelledTextInput(objects[0].tile_coord.to_string()));
				
				if (objects[0] is ActivatorTile)
				{
					var activator_tile:ActivatorTile = objects[0] as ActivatorTile;
					
					this.editor.edit_menu.add_edit_object(new EditObject(objects[0], "id", int));
					this.editor.edit_menu.add_edit_object(new EditObject(this.game.activator_sets[activator_tile.id], "delay", Number));
				}				
				
				if (objects[0] is ActivatedTile)
				{
					var activated_tile:ActivatedTile = objects[0] as ActivatedTile;
					
					this.editor.edit_menu.add_edit_object(new EditObject(objects[0], "id", int));					
				}					
				
				if (objects[0] is TeleportTile)
				{
					var teleport_tile:TeleportTile = objects[0] as TeleportTile;
					
					this.editor.edit_menu.add_edit_object(new EditObject(teleport_tile.link, "x", int));					
					this.editor.edit_menu.add_edit_object(new EditObject(teleport_tile.link, "y", int));					
					this.editor.edit_menu.add_edit_object(new EditObject(teleport_tile.link, "z", int));					
				}				
			}	
				
			this.editor.edit_menu.close_button.addEventListener(MouseEvent.MOUSE_DOWN, edit_complete_hnd);
		}		
		
		
		
		private function edit_complete_hnd(e:Event):void
		{
			this.game.removeChild(this.editor.edit_menu);
			this.editor.edit_menu.apply_edits();
			this.editor.edit_menu.clear();
		}
		
		
		
		private function click_handler(e:MouseEvent3D):void
		{				
			this.game.mouse_picker_plane.addEventListener(MouseEvent3D.MOUSE_MOVE, drag_handler);
		}
		
				
		
		private function drag_handler(e:MouseEvent3D):void		
		{				
			var x:int = int(Math.round(e.scenePosition.x / Game.TILE));
			var z:int = int(Math.round(e.scenePosition.z / Game.TILE));	
			var brush_size:int = this.editor.brush_menu.brush_size;
			
			for (var x_i:int = Math.max(-Game.ARRAY_OFFSET,Math.round(x - brush_size/2)); x_i <= Math.min(Game.ARRAY_OFFSET-1, Math.round(x + brush_size/2)); x_i++)
			for (var z_i:int = Math.max(-Game.ARRAY_OFFSET,Math.round(z - brush_size/2)); z_i <= Math.min(Game.ARRAY_OFFSET-1, Math.round(z + brush_size/2)); z_i++)
			{
				populate_state_array(new TileCoord3D(x_i, 0, z_i));	
			}
		}	
		
		
		
		private function populate_state_array(p:TileCoord3D):void
		{
			FlashConnect.trace(p.x);
			var tile_xml:XML = <tile/>;
			
			tile_xml.@x = p.x;
			tile_xml.@y = 0;
			tile_xml.@z = p.z;
			tile_xml.@val = this.editor.brush_menu.selected_brush;
			
			if (this.editor.brush_menu.selected_brush == TileState.TELEPORT_TILE)
			{
				tile_xml.appendChild( <teleport x="0" y="0" z="0" />);
			}			
			if (this.editor.brush_menu.selected_brush == TileState.ACTIVATED)
			{
				tile_xml.appendChild( <activated id="0" />);
			}
			if (this.editor.brush_menu.selected_brush == TileState.ACTIVATOR)
			{
				tile_xml.appendChild( <activator id="0" delay="-1"/>);
			}
			
			this.game.populate_tile(tile_xml);			
		}
		
		
		
		private function release_handler(e:Event):void
		{			
			this.game.mouse_picker_plane.removeEventListener(MouseEvent3D.MOUSE_MOVE, drag_handler);
		}		
		
		
		
		private function game_key_handler(e:KeyboardEvent):void
		{	
			if (input_enabled)
			{
				make_move(e.keyCode);
			}
			else
			{
				if (this.input_buffer.length < this.MAX_INPUT_BUFFER_LENGTH)
				{
					if (this.input_buffer.indexOf(e.keyCode) == -1)					
						this.input_buffer.push(e.keyCode);	
					else if (this.input_buffer.length < this.MAX_INPUT_BUFFER_LENGTH-2)
						this.input_buffer.push(e.keyCode);
				}
			}
		}	
		
		
		private function make_move(key:uint):void
		{
				switch (key)
				{							
					
					case 97 :	// Move +Z
								{	
									disable_game_input();
									this.game.start_move(TileCoord3D.SW_VECTOR);										
									break;
								}
					case 99 :	// Move +X
								{
									disable_game_input();
									this.game.start_move(TileCoord3D.SE_VECTOR);																	
									break;
								}
					case 103:	// Move -X
								{
									disable_game_input();
									this.game.start_move(TileCoord3D.NW_VECTOR);								
									break;
								}
					case 105:	// Move -Z
								{
									disable_game_input();
									this.game.start_move(TileCoord3D.NE_VECTOR);									
									break;
								}		
				}			
		}
		
		

		private function edit_key_handler(e:KeyboardEvent):void
		{		
			FlashConnect.trace(e.keyCode);
			switch (e.keyCode)
			{							
				case 37 :
							{								
								this.game.shift_state_array(Game.ARRAY_SIZE-1, 1, -1, new TileCoord3D( 0, 0, -1));
								this.game.build_level(this.game.state_array_to_xml(this.game.state_array));																												
								break;
							}					
							
				case 38 :
							{
								this.game.shift_state_array(Game.ARRAY_SIZE-1, 1, -1, new TileCoord3D(-1, 0, 0));
								this.game.build_level(this.game.state_array_to_xml(this.game.state_array));															
								break;
							}							
							
				case 39 :
							{		
								this.game.shift_state_array(1, Game.ARRAY_SIZE-1, 1, new TileCoord3D(0, 0, 1));
								this.game.build_level(this.game.state_array_to_xml(this.game.state_array));
								break;
							}
							
				case 40 :
							{
								this.game.shift_state_array(1, Game.ARRAY_SIZE-1, 1, new TileCoord3D(1, 0, 0));
								this.game.build_level(this.game.state_array_to_xml(this.game.state_array));						
								break;
							}
							
				case 71 :	// generate random level
							{
								var gen:RandomGenerator = new RandomGenerator();						
								this.game.build_level(this.game.state_array_to_xml(gen.get_level()));
								break;
							}
				
				
				case 83:	// save level's state in xml format
							{
								if (e.ctrlKey == true)
									this.game.save_state_xml();
								break;
							}
				case 32:	// brush select
							{								
								if (this.editor.brush_menu_visible == false)
								{									
									this.game.addChild(this.editor.brush_menu);
									this.editor.brush_menu_visible = true;
								}
								else
								{
									this.game.removeChild(this.editor.brush_menu);
									this.editor.brush_menu_visible = false									
								}
								break;
							}	
				case 187:
							{
								if (editor.brush_menu.brush_size < 4)
									editor.brush_menu.brush_size++;
								break;
							}					
				case 189:
							{
								if (editor.brush_menu.brush_size > 0)
									editor.brush_menu.brush_size--;
								break;
							}							
							
				case 190:
							{
								this.game.current_level++;
								this.game.current_level%=this.game.level_list.length;
								this.game.build_level(this.game.load_state_xml(this.game.level_list[this.game.current_level]));
								break;
							}					
				case 188:
							{
								this.game.current_level+=(this.game.level_list.length-1);
								this.game.current_level%=this.game.level_list.length;
								this.game.build_level(this.game.load_state_xml(this.game.level_list[this.game.current_level]));
								break;
							}							
							
			}
		}	
		
		
		private function touch_handler(e:TouchEvent):void
		{				
			if (e.stageX > this.game.stage.stageWidth/2 && e.stageY > this.game.stage.stageHeight/2)
			{									
				this.game.start_move(new TileCoord3D(-1, 0, 0));																
			}				
			if (e.stageX < this.game.stage.stageWidth/2 && e.stageY > this.game.stage.stageHeight/2)
			{									
				this.game.start_move(new TileCoord3D(0, 0, 1));												
			}				
			if (e.stageX > this.game.stage.stageWidth/2 && e.stageY < this.game.stage.stageHeight/2)
			{									
				this.game.start_move(new TileCoord3D(0, 0, -1));												
			}				
			if (e.stageX < this.game.stage.stageWidth/2 && e.stageY < this.game.stage.stageHeight/2)
			{									
				this.game.start_move(new TileCoord3D(1, 0, 0));		
			}										
		}			
		
		
	}
}